package com.ruoyi.ysVideo.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.Threads;
import com.ruoyi.detection.domain.RiceCanopyDetectionLog;
import com.ruoyi.detection.service.RiceCanopyService;
import com.ruoyi.utils.FileTool;
import com.ruoyi.utils.MyDateUtil;
import com.ruoyi.ysVideo.domain.*;
import com.ruoyi.ys7Video.domain.VideoDao;
import com.ruoyi.ysVideo.mapper.EzvizVideoAutoTaskMapper;
import com.ruoyi.ysVideo.service.EzvizService;
import com.ruoyi.ysVideo.service.EzvizVideoAutoTaskService;
import com.ruoyi.ysVideo.utls.MyTools;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.entity.ContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ScheduledFuture;

/**
 * @program: my-ruoyi-master_202130504106_李航
 * @description:
 * @author: LiHang
 * @create: 2022-01-10 16:12
 **/

@Service
@Slf4j
public class EzvizVideoAutoTaskServiceImpl implements EzvizVideoAutoTaskService {

    /**
     * 萤石云 Token Url
     */
    @Value("${ys7.ys7tokenUrl}")
    private String ys7tokenUrl;

    /**
     * 萤石云 获取-播放地址 Url
     */
    @Value("${ys7.ys7LiveAddress}")
    private String ys7LiveAddress;

    /**
     * 萤石云 抓拍图片地址
     */
    @Value("${ys7.ys7CaptureUrl}")
    private String ys7CaptureUrl;

    /**
     * 萤石云 添加预置位
     */
    @Value("${ysOperation.ys7PresetAdd}")
    private String ys7PresetAdd;

    /**
     * 萤石云 调用预置位
     */
    @Value("${ysOperation.ys7PresetMove}")
    private String ys7PresetMove;


    /**
     * 萤石云 清除预置位
     */
    @Value("${ysOperation.ys7PresetClear}")
    private String ys7PresetClear;

    /**
     * 存储文件的路径
     */
    @Value("${ruoyi.profile}")
    private String profile;


    @Autowired
    private ThreadPoolTaskScheduler myThreadPoolTaskScheduler;

    @Autowired
    private EzvizVideoAutoTaskMapper videoAutoTaskMapper;

    @Autowired
    private RiceCanopyService riceCanopyService;

    @Autowired
    private EzvizService ezvizService;


    /**
     *
     * ThreadPoolTaskScheduler：线程池任务调度类，能够开启线程池进行任务调度。
     * ThreadPoolTaskScheduler.schedule()方法会创建一个定时计划ScheduledFuture，
     * 在这个方法需要添加两个参数，Runnable（线程接口类） 和CronTrigger（定时任务触发器）
     * @return
     */
//    @Bean
//    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
//        ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
//        return new ThreadPoolTaskScheduler();
//    }

    /**
     * 在ScheduledFuture中有一个cancel可以停止定时任务。
     */
    private HashMap<Integer, ScheduledFuture<?>> futures = new HashMap<Integer, ScheduledFuture<?>>();

    /**
     * 项目启动时，初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理（注：不能手动修改数据库ID和任务组名，否则会导致脏数据）
     */
    @PostConstruct
    public void init(){
        // 如果是windows系统，则不继续执行调度定时任务
        if(MyTools.isWindowsSystem()){
            return;
        }
        List<MyVideoAutoTask> myVideoAutoTasks = videoAutoTaskMapper.selectAllTask();
        for (MyVideoAutoTask myVideoAutoTask : myVideoAutoTasks) {
            futures.put(myVideoAutoTask.getId(),
                    myThreadPoolTaskScheduler.schedule(() -> {
                        try {
                            capture(myVideoAutoTask);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }, new CronTrigger(myVideoAutoTask.getCron())));
        }
    }

    /**
     * 获取所有的定时任务信息
     * @return
     */
    @Override
    public List<MyReturnVideoAutoTask> selectAllTask() {
        return changeCronList(videoAutoTaskMapper.selectAllTask());
    }

    @Override
    public List<MyReturnVideoAutoTask> selectAllTaskBydeviceSerial(MyVideoAutoTask myVideoAutoTask) {
        return changeCronList(videoAutoTaskMapper.selectAllTaskBydeviceSerial(myVideoAutoTask));
    }

    /**
     * 新增定时任务
     * @param myVideoAutoTask
     * @return
     */
    @Override
    public int insertAutoTask(MyVideoAutoTask myVideoAutoTask) {
        int count = videoAutoTaskMapper.insertAutoTask(myVideoAutoTask);
        int id = myVideoAutoTask.getId();
        futures.put(id,myThreadPoolTaskScheduler.schedule( () -> {
            try {
                capture(myVideoAutoTask);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }, new CronTrigger(myVideoAutoTask.getCron())));
        return id;

    }

    /**
     * 根据id关闭定时任务
     * @param id
     * @return
     */
    @Override
    public int deleteAutoTask(Integer id) {
        //System.out.println(futures);
        ScheduledFuture<?> scheduledFuture = futures.get(id);
        //System.out.println(scheduledFuture);
        futures.remove(id);
        scheduledFuture.cancel(true);
        return videoAutoTaskMapper.deledeAutoTask(id);
    }

    /**
     * 定时任务内容
     * 调用预置位，进行拍摄
     */
    public void capture(MyVideoAutoTask myVideoAutoTask) throws InterruptedException, IOException {
        // 判断如果是windows系统则不执行下述逻辑
        if(MyTools.isWindowsSystem()){
            return;
        }
        //先通过app_key和app_secret获取token
        AppInfo info = ezvizService.selectMyAppInfo(myVideoAutoTask.getDeviceSerial());
        MyToken myToken = MyTools.get_access_token(info.getAppKey(),info.getAppSecret());
//        String getLiveAddressUrl = ys7LiveAddress + "?accessToken=" + videoDao.getAccessToken() + "&deviceSerial=" + videoDao.getDeviceSerial();
        String getLiveAddressUrl = ys7PresetMove + "?accessToken=" + myToken.getAccess_token() + "&deviceSerial=" + myVideoAutoTask.getDeviceSerial() + "&channelNo=1&index=" + myVideoAutoTask.getPresetIndexId();
        log.info(getLiveAddressUrl);
        try {
            ResponseEntity<String> responseEntity = Threads.postRequest(getLiveAddressUrl);
            /* 拿到data数据 */
            JSONObject jsonObject = JSONObject.parseObject(responseEntity.getBody());
            String code=jsonObject.get("code").toString();
            if(code.equals("200")){
                log.info("成功成功预置位");
            }else {
                log.info("未知错误，请联系管理员，错误码："+code);
            }

        } catch (Exception e) {
            System.out.println(e);
            log.info("调用预置位出错！请重试!");
        }
//        Thread.sleep(4000);
        Thread.sleep(15000);
        /* 拼接获取token的URL */
        String getTokenUrl = ys7CaptureUrl + "?accessToken=" + myToken.getAccess_token() + "&deviceSerial=" + myVideoAutoTask.getDeviceSerial() + "&channelNo=1";
        VideoDao videoDao = null;
        try {
            ResponseEntity<String> responseEntity = Threads.postRequest(getTokenUrl);
            /* 拿到data数据 */
            JSONObject jsonObject = JSONObject.parseObject(responseEntity.getBody());
            String s = jsonObject.getJSONObject("data").toString();
            videoDao = JSON.parseObject(s, VideoDao.class);
            log.info(videoDao.toString());
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        if (StringUtils.isNotNull(videoDao)) {

            String path = profile + File.separator + "capture" + File.separator + myVideoAutoTask.getDeviceSerial();
            String date = MyDateUtil.dateFormate(new Date(), "yyyy_MM_dd_HH_mm_ss");
            String fileNme = myVideoAutoTask.getDeviceSerial() + "_" + date + ".jpg";
            // 存放结果路径
            String resultPath = null;
            MyVideoImg imgInfo = new MyVideoImg();
            boolean isSuccess = FileTool.downloadImageByUrl(videoDao.getPicUrl(), path, fileNme);
            if (isSuccess) {

                resultPath = "profile" +File.separator + "capture" + File.separator + myVideoAutoTask.getDeviceSerial() + File.separator + fileNme;
                imgInfo.setDeviceSerial(myVideoAutoTask.getDeviceSerial());
                imgInfo.setPicUrl(resultPath);

                // 尝试抓拍图片通过水稻冠层检测，并将结果图路径放入球机抓拍表中=====
                // 构造路径
                Path path1 = Paths.get(path +"/"+fileNme);
                // 识别返回
                List<RiceCanopyDetectionLog> ret_lst = riceCanopyService.insertRiceCanopyDetectionLog(
                        new MockMultipartFile(
                                fileNme, //文件名
                                fileNme, //originalName 相当于上传文件在客户机上的文件名
                                ContentType.APPLICATION_OCTET_STREAM.toString(), //文件类型
                                Files.newInputStream(path1) //文件流
                        )
                        , imgInfo.getDeviceSerial());
                // 判断是否有结果图路径，没有就赋值为抓拍图片路径=====
                if(ret_lst!=null){
                    if(ret_lst.size()>0){
                        imgInfo.setResUrl(ret_lst.get(0).getResultPath().replace("_Result","__Result"));
                        imgInfo.setTag(ret_lst.get(0).getTag());
                    }else{
                        imgInfo.setResUrl(resultPath);
                        imgInfo.setTag("{}");
                    }
                }else{
                    imgInfo.setResUrl(resultPath);
                    imgInfo.setTag("{}");
                }
                ezvizService.insertMyCaptureImg(imgInfo);
            }
//            // 构造路径
//            Path path1 = Paths.get(path +"/"+fileNme);
//            System.out.println(path1);
//            // 识别返回
//            riceCanopyService.insertRiceCanopyDetectionLog(
//                    new MockMultipartFile(
//                            fileNme, //文件名
//                            fileNme, //originalName 相当于上传文件在客户机上的文件名
//                            ContentType.APPLICATION_OCTET_STREAM.toString(), //文件类型
//                            Files.newInputStream(path1) //文件流
//                    )
//                    , myVideoAutoTask.getDeviceSerial());
            log.info(String.valueOf(isSuccess ? AjaxResult.success("保存图片成功，保存至默认路径!", resultPath) : AjaxResult.error("保存图片出错！")));
        } else {
            log.info("抓拍图片出错！");
        }
    }

    /**
     * 生成返回的信息，具体为将cron改为可理解的信息
     * @param myVideoAutoTasks
     * @return
     */
    public List<MyReturnVideoAutoTask> changeCronList(List<MyVideoAutoTask> myVideoAutoTasks){
        List<MyReturnVideoAutoTask> myReturnVideoAutoTasks = new ArrayList<>();
        for (MyVideoAutoTask myVideoAutoTask : myVideoAutoTasks) {
            String[] crons = myVideoAutoTask.getCron().split(" ");
            String hour=crons[2];
            String minute=crons[1];
            String second=crons[0];
            MyReturnVideoAutoTask myReturnVideoAutoTask = new MyReturnVideoAutoTask();
            myReturnVideoAutoTask.setHour(hour);
            myReturnVideoAutoTask.setMinute(minute);
            myReturnVideoAutoTask.setSecond(second);
            myReturnVideoAutoTask.setId(myVideoAutoTask.getId());
            myReturnVideoAutoTask.setPresetIndexId(myVideoAutoTask.getPresetIndexId());
            myReturnVideoAutoTask.setDeviceSerial(myVideoAutoTask.getDeviceSerial());
            myReturnVideoAutoTasks.add(myReturnVideoAutoTask);
        }
        return myReturnVideoAutoTasks;
    }

    //返回符合条件的定时任务id-list
    public List<Integer> existAutoTask(MyGetVideoAutoTask myGetVideoAutoTask){
        return videoAutoTaskMapper.existAutoTask(myGetVideoAutoTask);
    }
}
